feat(webauthn): largeBlob read via authenticatorLargeBlobs#206
Open
AlfioEmanueleFresta wants to merge 3 commits into
Open
feat(webauthn): largeBlob read via authenticatorLargeBlobs#206AlfioEmanueleFresta wants to merge 3 commits into
AlfioEmanueleFresta wants to merge 3 commits into
Conversation
4f484fb to
63fd2d2
Compare
1129c2d to
5a50879
Compare
AlfioEmanueleFresta
added a commit
that referenced
this pull request
May 12, 2026
Per review on #198: keep the per-credential largeBlobKey only on the CTAP-level Ctap2GetAssertionResponse. Surfacing it on the public Assertion struct gives callers a foot-gun to forward straight to the RP, which is exactly the disclosure this PR is meant to prevent. The follow-up authenticatorLargeBlobs PR (#206) can read the key directly off the CTAP response.
63fd2d2 to
34571aa
Compare
AlfioEmanueleFresta
added a commit
that referenced
this pull request
May 12, 2026
Per review on #198: keep the per-credential largeBlobKey only on the CTAP-level Ctap2GetAssertionResponse. Surfacing it on the public Assertion struct gives callers a foot-gun to forward straight to the RP, which is exactly the disclosure this PR is meant to prevent. The follow-up authenticatorLargeBlobs PR (#206) can read the key directly off the CTAP response.
34571aa to
5388273
Compare
AlfioEmanueleFresta
added a commit
that referenced
this pull request
May 12, 2026
Per [WebAuthn L3 sec. 10.1.5 (largeBlob extension)](https://www.w3.org/TR/webauthn-3/#sctn-large-blob-extension), the relying party expects the `blob` output to be the decrypted plaintext blob payload, fetched by the platform via the CTAP [`authenticatorLargeBlobs`](https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorLargeBlobs) command using the per-credential `largeBlobKey` as an AES-256-GCM key. The library was instead writing the raw `largeBlobKey` into `blob` and never calling `authenticatorLargeBlobs`. That means an RP receives the AES key itself (not the blob), and if the RP can also read the device's `largeBlobArray` (publicly readable region of the authenticator over CTAP), it can decrypt and forge entries. Until `authenticatorLargeBlobs` is wired up (follow-up PR #206), the safe behaviour is to set `large_blob.blob = None`. The CTAP-level model keeps the field so the follow-up can use it. ## Changes - Stop routing `largeBlobKey` into the WebAuthn `large_blob.blob` output. - The CTAP-level `Ctap2GetAssertionResponse.large_blob_key` remains so the next PR can use it. - Regression test asserts the WebAuthn response no longer contains the key. ## References - [WebAuthn L3 sec. 10.1.5: Large blob storage extension (`largeBlob`)](https://www.w3.org/TR/webauthn-3/#sctn-large-blob-extension) - [CTAP 2.1 sec. 6.10: `authenticatorLargeBlobs` (0x0C)](https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorLargeBlobs)
5a50879 to
33be166
Compare
33be166 to
82e3197
Compare
82e3197 to
c70b88a
Compare
Implements the wire-level model and protocol method for CTAP 2.1 `authenticatorLargeBlobs` (command code 0x0C, spec §6.10). This is the device-side primitive the platform uses to fetch and update the authenticator's serialized largeBlobArray. Includes only the `get` request shape so far; `set` is reserved for a follow-up that will also handle the pinUvAuthParam binding required for writes. Refs: CTAP 2.2 §6.10.
Adds the read-path helper for the WebAuthn L3 largeBlob extension. After get_assertion returns a largeBlobKey, the platform paginates authenticatorLargeBlobs(get), AES-256-GCM-authenticates each entry under the per-credential key, and RFC 1951 raw-deflate decompresses the plaintext into unsigned_extensions_output.large_blob.blob. Read failures surface as blob absent, per WebAuthn L3 sec 10.1.5. The chunk size honours maxFragmentLength = maxMsgSize - 64 from GetInfo. origSize is capped at 1 MiB to bound platform allocation. Per-entry structural problems are skipped, not propagated, since the on-device array is shared across credentials.
Enables largeBlobs storage on the virt fido-authenticator and adds an end-to-end test: registers a credential, plants an encrypted entry directly via authenticatorLargeBlobs(set), then asserts webauthn_get_assertion with largeBlob.read surfaces the plaintext. Requires the chunked feature on fido-authenticator to lift the 1024-byte single-message cap on the device-wide array, and a non-zero max_msg_size in the virt config so the per-chunk length check passes.
c70b88a to
3e87620
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the read half of the WebAuthn L3
largeBlobextension end to end. Until now the library could request a per-credential key from the authenticator but had no way to actually fetch and decrypt the stored blob, so the read output was never populated.After an assertion succeeds and yields a per-credential AES key, the platform paginates
authenticatorLargeBlobs(get), AES-256-GCM-authenticates each on-device entry under that key, and RFC 1951 raw-deflate decompresses the plaintext into the assertion response. Read failures are non-fatal and surface as an absent blob, per WebAuthn L3 §10.1.5.Spec compliance
maxFragmentLength = maxMsgSize - 64from GetInfo (CTAP 2.1 §6.10.2)origSizeis capped at 1 MiB to bound platform allocationTests
origSizeskipFollow-ups
The write half lands in the next PR: chunked CTAP upload with
pinUvAuthParam, fetch-modify-write of the on-device array, and the WebAuthnlargeBlob.writeextension wiring.Spec references